查看原文
其他

当创建对象时......

码农翻身刘欣 码农翻身 2020-10-08

"程序员要创建对象了,快去西天请Spring'佛祖'。"  一大早我就听到Tomcat在那里大喊。


“不就是创建一个对象吗,你给JVM老大打个电话,请他在堆里边new出来不就结了?无非就是一片儿内存而已。”我问道。


“唉,你不知道,这不仅仅是对象的创建,更是对象的装配,要依赖注入,要初始化,又要代理什么的..... 这事儿Spring最擅长。”Tomcat感慨到。


Spring慢悠悠地来了:“想安静地喝一会儿茶都不行!让我瞧瞧,这个要创建的对象是单例吗? 嗯,果然是单例,也许之前创建过!让我从我的缓存中找找有没有! ”


Spring在他的缓存中扒拉半天,没有找到,不满地说:“缓存中没有,看来得忙碌一番了。”


他请JVM老大把要创建对象的类PetStoreService 从方法区取过来, 检查了一下,叹了口气。


我问道:“先生为何发愁啊?”


Spring说:“这个类没有缺省构造函数,你看看,它依赖AccountDao和ItemDao,我还得先把这两个bean给创建起来,然后才能调用这个构造函数创建这个PetStoreService对象,很麻烦的。”


public PetStoreService(AccountDao accountDao, 
       ItemDao itemDao){
    this.accountDao = accountDao;
    this.itemDao = itemDao;
}


我估计这个所谓的bean 就是一个java对象, 怪不得Tomcat说Spring做的主要是装配工作,此言不虚啊。


Spring 把当前的创建工作先放下,把工作中心转移到AccountDao, 照例还是要先看构造函数,这次运气不错,有个缺省的、无参数的构造函数。


我说:“这下可以把它给new 出来了吧!”


Spring说:“不不,在让JVM把它new 出来(这叫实例化)之前,我需要看看程序员有没有给我设置一些需要预先执行的代码, 如果有,我得先执行。 同理,实例化之后,还得做类似事情。”




Spring先执行了所谓“前置代码”,然后用反射的方式通知JVM把对象给创建起来。


注: 事实上在JVM那里还有一番折腾:


(1) 先执行AccountDao中实例变量的初始化 

(2) 执行实例代码块 

(3) 最后才是执行构造函数,把AccountDao创建起来,返回给Spring。


拿到了AccountDao的实例,Spring马上执行"实例化"以后的"后置代码"。


我对他深表同情:“不容易,这样一个对象就ok了,可以返回给程序员了吧?”


Spring说道:“哪有那么简单!还有一步,叫做初始化,需要调用程序员指定的初始化方法。”


“初始化的前后也需要调用程序员设置的代码吧?”


“没错,就是这样,哎呀,你看看,这个AccountDao中还有@Autowired注解,需要注入一个User对象,我还得处理一下,真是麻烦!”


Spring再次放下手头工作,开始创建User对象,还是实例化,初始化,前置代码,后置代码,唉,这是一个递归的过程,我都懒得看了。


终于AcountDao对象创建完毕,接下来是ItemDao对象,又是一番同样的折腾。


手持AccountDao对象和ItemDao对象, Spring终于可以开始创建PetStoreService对象了。


实例化,初始化,前置代码调用,后置代码调用, 都是熟悉的配方、熟悉的味道。


PetStoreService的对象已经创建出来(简称petStore),也已经初始化完毕,只剩下最后一步:初始化的后置代码调用。


Spring看了看相关配置,心里咯噔了一下,说到:“坏了,这个PetStoreService 用了声明式事务,我还得创建一个代理出来(简称petStoreProxy),在这个proxy中来调用事务相关的代码。”


“怎么创建代理对象?” 我问道。


“我看看这个程序员想要的是petStore的儿子还是兄弟。”


“儿子? 兄弟?”


“哈哈,那是我的做的一个比喻,儿子就是这个PetStoreService类没有实现接口, 我只好新创建一个类PetStoreServiceProxy,去继承PetStoreService。 兄弟就是我创建的类PetStoreServiceProxy 和PetStoreService都实现同样的接口。”



Spring看了看:“嗯,看来是'儿子',JVM老兄,你去把CGLib叫来吧,我得请他在运行时生成一个新的Class,这个新的Class要继承PetStoreService。”


CGLib是个热情洋溢的小伙子,迅速地在内存中创建了新的字节码, 把我看得目瞪口呆,这个世界上还真有直接写字节码的人。


新的Class PetStoreProxy的字节码已经准备好,JVM把他装载到方法区,Spring用它创建了一个对象出来,终于返回给了程序员!


“程序员拿到的对象可不是PetStoreService啊,他会发现的吧?” 我善意地提醒Spring。


“没关系,两个类的'接口'都是相同的,除非他检查对象的所属类,否则是意识不到的。再说了,哪个程序员会闲得无聊去查看对象所属的类啊。”


"佛祖"Spring折腾了半天, 又去悠哉游哉地喝茶去了。


(完)

码农翻身,用故事讲解技术本质, 更多精彩文章,请移步《码农翻身三年文章精华

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存